using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace Socket_Client
{
    class AsyncSocket : IDisposable
    {
        int _Port;
        public int Port { get { return _Port; } }
        static int _BufferSize;
        public static int BufferSize { get { return _BufferSize; } }
        /// <summary>
        /// 接收连续错误次数
        /// </summary>
        int _StartReceiveServiceErrorRetryCount;

        bool _IsReceiving = false;
        public bool IsReceiving { get { return _IsReceiving; } }

        Socket aSocket;

        public delegate void DelegateWorking(AsyncSocket sender, SocketState state);

        /// <summary>
        /// 开始接收数据
        ///  void DelegateWorking(AsyncSocket sender, SocketState state)
        /// </summary>
        public event DelegateWorking OnWaitingReceive;
        /// <summary>
        /// 接收数据完成.
        ///  void DelegateWorking(AsyncSocket sender, SocketState state)
        /// </summary>
        public event DelegateWorking OnReceived;

        /// <summary>
        /// 发送数据完成.
        ///  void DelegateWorking(AsyncSocket sender, SocketState state)
        /// </summary>
        public event DelegateWorking OnSent;
        public delegate void DelegateError(AsyncSocket sender, SocketState state, string errorMessage);
        /// <summary>
        /// 接收错误.
        /// void DelegateError(AsyncSocket sender, SocketState state, string errorMessage)
        /// </summary>
        public event DelegateError OnReceivingError;
        /// <summary>
        /// 发送错误.
        /// void DelegateError(AsyncSocket sender, SocketState state, string errorMessage)
        /// </summary>
        public event DelegateError OnSendError;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="port">接收和发送端口</param>
        /// <param name="bufferSize">缓冲区最小大小</param>
        public AsyncSocket(int port, int bufferSize, int startReceiveServiceErrorRetryCount)
        {
            _Port = port;
            _BufferSize = bufferSize;
            _StartReceiveServiceErrorRetryCount = startReceiveServiceErrorRetryCount;

            IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, Port);

            aSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            aSocket.Bind(localEndPoint);
        }

        public void Dispose() { aSocket.Close(); }

        public void StartReceiving()
        {
            bool isSucceeded = false;
            SocketState state = new SocketState();
            for (int i = _StartReceiveServiceErrorRetryCount; i > 0; i--)
            {
                try
                {
                    aSocket.BeginReceiveFrom(state.Buffer, 0, state.BufferSize, SocketFlags.None, ref state.RemoteEndPoint,
                                        new AsyncCallback(AsyncCallback_ReceiveFrom), state);
                    _IsReceiving = true;
                    isSucceeded = true;
                    if (OnWaitingReceive != null) OnWaitingReceive(this, state);
                    break;
                }
                catch (SocketException e)
                {
                    _IsReceiving = false;
                    if (OnReceivingError != null) OnReceivingError(this, state, e.Message);
                }
            }
            if (!isSucceeded) OnReceivingError(this, state, "接收连续错误次数超过限制! 停止接收.");
        }

        void AsyncCallback_ReceiveFrom(IAsyncResult asyncResult)
        {
            bool isSucceeded = false;
            SocketState state = (SocketState)asyncResult.AsyncState;
            try
            {
                state.MessageLength = aSocket.EndReceiveFrom(asyncResult, ref state.RemoteEndPoint);
                _IsReceiving = false;
                isSucceeded = true;
            }
            catch (SocketException e) { ReceiveError(state, e.Message); }
            finally { StartReceiving(); }

            if (isSucceeded)
            {
                if (OnReceived != null) OnReceived(this, state);
            }
        }

        void ReceiveError(SocketState state, string message)
        {
            if (OnReceivingError != null) OnReceivingError(this, state, message);
        }

        public void SendTo(IPAddress remoteIP, string message)
        {
            SendTo(new IPEndPoint(remoteIP, Port), message);
        }

        public void SendTo(EndPoint remoteEndPoint, string message)
        {
            SocketState state = new SocketState(remoteEndPoint, message);
            try
            {
                aSocket.BeginSendTo(state.Buffer, 0, state.MessageLength, SocketFlags.None, remoteEndPoint, new AsyncCallback(AsyncCallBack_SendTo), state);
                if (OnSent != null) OnSent(this, state);
            }
            catch (SocketException e)
            {
                SendError(state, e.Message);
            }
        }

        void AsyncCallBack_SendTo(IAsyncResult asyncResult)
        {
            int sendLength = aSocket.EndSendTo(asyncResult);
            SocketState state = (SocketState)asyncResult.AsyncState;
            if (state.MessageLength == sendLength && asyncResult.IsCompleted) { if (OnSent != null) OnSent(this, state); }
            else if (state.MessageLength != sendLength) SendError(state, "数据长度不一致!");
            else if (!asyncResult.IsCompleted) SendError(state, "发送数据未完成!");
        }

        void SendError(SocketState state, string message)
        {
            if (OnSendError != null) OnSendError(this, state, message);
        }
    }
}
